
var Jx = function(){};

/**
 *	Finalize Global name
 */
Jx.GLOBAL = this;


(function(J, O, A){


/**
 *	private vars
 */
var _ids = {},

	_enumerables = (function(prop){

		var o = {},

			filter = o.propertyIsEnumerable;

		for(var n, l = prop.length; l--;){

			o[ n = prop[l] ] = 'n';

			if (filter.call(o, n)){

				prop.splice(l, 1);

			}

		}

		return prop;

	})([

		'hasOwnProperty',

		'valueOf',

		'isPrototypeOf',

		'propertyIsEnumerable',

		'toLocaleString',

		'toString',

		'constructor'

	]);

/**
 *	private functions
 */
function _assignObject(name, value, obj){

	this[name] = value;

}

function _assignIfObject(name, value, obj){

	if (!(name in this) || typeof this[name] == 'undefined'){

		this[name] = value;

	}

}

function _classOverride(name, value, property){

	var Class = this,

		override, proto;

	if (property.hasOwnProperty(name)){

		//-- own method
		if (O.toString.call(value) == '[object Function]'){

			if (name == 'constructor'){

				name = '$construct';

			}

			//-- bootstrap method
			if (value.hasOwnProperty('$class') && value.$class !== Class){

				var __callee__ = value;

				value = function(){

					return __callee__.apply(this, arguments);

				}

			}

			proto = Class.prototype;

			if (!value.$overridden &&

				proto.hasOwnProperty(name) &&

				O.toString.call(override = proto[name]) == '[object Function]'

			){

				value.$overridden = override;

			}

			value.$class = Class;

			value.$method = name;

		}

		Class.prototype[name] = value;

	}

}

/**
 *	public methods
 */

/**
 *	Array Fixes
 */
	//-- fix array that doesn't support indexOf
	if (!A.indexOf){

		A.indexOf = function(o, from){

			var my = this,

				l = my.length;

			from = J.isNumber(from) ?

					from

					:

					0;

			from = (from < 0) ?

				Math.ceil(from)

				:

				Math.floor(from);

			if (from < 0){

				from += l;

			}

			for (; from < l; from++) {

				if ( (from in my) && (my[from] === o) ){

					return from;

				}
			}

			return -1;

		}
	}

	if (!A.lastIndexOf){

		A.lastIndexOf = function(o, from){

			var my = this,

				l = my.length;

			if (J.isNumber(from)){

				from = (from < 0) ?

					Math.ceil(from)

					:

					Math.floor(from);

				if (from < 0) {

					from += l;

				} else if (from >= l){

					from = l - 1;

				}
			} else {

				from = l - 1;

			}


			for (; from > -1; from--) {

				if ((from in my) && (my[from] === o)){

					return from;

				}

			}

			return -1;
		};


	}

	// Production steps of ECMA-262, Edition 5, 15.4.4.18
	if (!A.forEach){

		A.forEach = function(fn, scope){

			var T,

				o = Object(this);

			if (!J.isFunction(fn)) {

				throw new TypeError();

			}

			if ( scope ) {

				T = scope;

			}

			for(var c=0, l = o.length >>> 0, name; c < l; c++) {

				if (c in o){

					fn.call( T, o[c], c, o );

				}

			}

		};

	}




/**
 *	basic methods
 */
J.each = function(o, fn, scope){

	var n;

	scope = scope || null;

	for(n in o){

		if (fn.call(scope, n, o[n], o) === false){

			break;

		}

	}

	if (o instanceof Object){

		for(var enums = _enumerables, l = enums.length; l--;){

			n = enums[l];

			if ((o.hasOwnProperty(n) || o[n] !== O[n]) &&

				fn.call(scope, n, o[n], o) === false

			){

				break;

			}

		}


	}

	return o;

}

J.assign = function(o1, o2, defaults){

	if (defaults){

		J.each(defaults, _assignObject, o1);

	}

	J.each(o2, _assignObject, o1);

	return o1;

}

J.extend = function(additional){

	var BaseClass = this,

		BasePrototype, TempClass;

	if (O.toString.call(additional) != '[object Object]'){

		additional = {};

	}

	//-- custom constructor
	function Class(){

		var instance = this,

			Class = arguments.callee;

		if (!(instance instanceof Class)){

			var Temp = new Function();

			Temp.prototype = Class.prototype;

			instance = new Temp();

		}

		instance.destroyed = false;

		instance.$cache = '$data' in Class ?

			new Class['$data']()

			:

			{};

		instance.initializeCache( instance.$cache );

		instance.$construct.apply(instance, arguments);

		return instance;

	}

	BasePrototype = (TempClass = function(){}).prototype = BaseClass.prototype;

	Class.prototype = new TempClass();

	J.assign(Class, {

		$class: Class,

		$base: BasePrototype,

		$method: 'constructor',

		extend: J.extend,

		override: J.override

	});

	Class.override(additional);

	Class.prototype.constructor = Class;

	return Class;

}

J.override = function(o){

	J.each(o, _classOverride, this);

}




/**
 *	static methods
 */
J.assign(J, {

	$class: J,

	$method: 'constructor',

	$base: {},

	$name: 'Jx',

	temporaryClassMethodCalls: [],

	assignIf: function(o1, o2){

		J.each(o2, _assignIfObject, o1);

		return o1;

	},

	id: function(prefix, remove){

		var ids = _ids,

			id = void(0),

			index = 0,

			m, cache;

		if (Jx.isString(prefix, true)){

			m = prefix.match(/([0-9]+)$/);

			if (m){

				index = parseInt(m = m[1], 10) || 0;

				prefix = prefix.substring( 0, prefix.length - m.length );

			}

		} else {

			prefix = null;

		}

		prefix = prefix || 'jx';

		if (!ids.hasOwnProperty(prefix)){

			(ids[prefix] = []).current = 0;

		}

		cache = ids[prefix];

		id = prefix + index;

		if (remove === true){

			if (index){

				ids[prefix].push( id );

			}

		} else {

			id = cache.length ?

				cache.shift()

				:

				prefix + ( ++ids[prefix].current );

		}

		return id;

	},

	isObject: function(o, nativeObject){
		
		var type = typeof o;
		
		return o != null && (
			
			nativeObject !== false ?
				
				O.toString.call(o) == '[object Object]' && o instanceof Object
				
				:
				
				type == 'object' || type == 'function'
			
		);

	},

	isString: function(o, notEmpty){

		return O.toString.call(o) == '[object String]' &&

			(notEmpty !== true || !!o);

	},

	isBoolean: function(o){

		return O.toString.call(o) == '[object Boolean]';

	},

	isNumber: function(o){

		return O.toString.call(o) == '[object Number]' && isFinite(o);

	},

	isScalar: function(o, includeBoolean){

		var type = O.toString.call(o);

		return type == '[object String]' ||

			(type == '[object Number]' && isFinite(o)) ||

			(!!includeBoolean && type == '[object Boolean]');

	},

	isFunction: function(o){

		return O.toString.call(o) == '[object Function]';

	},

	isArray: function(o, notEmpty){

		return O.toString.call(o) == '[object Array]' &&

			(notEmpty !== true || !!o.length);

	},

	isDate: function(o){

		return O.toString.call(o) == '[object Date]';

	},

	isRegExp: function(o){

		return O.toString.call(o) == '[object RegExp]';

	},

	createError: function(type){

		var GLOBAL = J.GLOBAL,

			Class = Error,

			messageIndex = arguments.length > 1 ?

				1

				:

				0;

		if (messageIndex){

			var camelized;

			if (J.isString(type, true) &&

				(camelized = type.charAt(0).toUpperCase() + type.substring(1, type.length) + 'Error') in GLOBAL

			){

				Class = GLOBAL[camelized];

			} else {

				messageIndex = 0;

			}

		}

		return new Class(

			Array.prototype.slice.call(arguments, messageIndex).join(' ')

		);

	},

	destroy: function(o){

		if (o instanceof Jx){

			o.destroy();

		} else if (Jx.isObject(o)){

			Jx.each(o, function(n, v, o){

				if (o.hasOwnProperty(n)){

					delete o[n];

				}

			});

		} else if (Jx.isArray(o)){

			o.splice(0, o.length);

		}

	},
/**
 *	Class Temporary Declarations
 */
	declare: function(){

		var o = Array.prototype.slice.call(arguments, 0);

		o.method = 'declare';

		Jx.temporaryClassMethodCalls.push( o );

	},

	categorize: function(){

		var o = Array.prototype.slice.call(arguments, 0);

		o.method = 'categorize';

		Jx.temporaryClassMethodCalls.push( o );

	},

	onDeclare: function(){

		var o = Array.prototype.slice.call(arguments, 0);

		o.method = 'onDeclare';

		Jx.temporaryClassMethodCalls.push( o );

	}

});

/**
 *	Jx Class methods
 */
J.override({

	$cache: null,

	destroyed: true,

	destroying: false,

	constructor: function(){},

	$super: function(args){

		var method = arguments.callee.caller,

			Class = method && method.$class,

			name = method && method.$method,

			Base = Class && Class.$base,

			fn = Base && name in Base && Base[name],

			nil;

		if (!fn){

			throw J.createError(

				!Class ?

					'cannot call $super outside the class methods'

					:

					'cannot call $super to non-existent Superclass method "'+method.$method+'"'

			);

			return nil;

		}

		return fn.apply(this, args || []);

	},

	$parent: function(args){

		var method = arguments.callee.caller,

			Class = method && method.$class,

			name = method && method.$method,

			fn = method && method.$overridden,

			nil;

		if (!fn){

			throw J.createError(

				!Class ?

					'cannot call $parent outside the class methods'

					:

					'cannot call $parent to non-existent Overridden method "'+method.$method+'"'

			);

			return nil;

		}

		return fn.apply(this, args || []);

	},

	$my: function(name, args){

		var method = arguments.callee.caller,

			Class = method && method.$class,

			nil;

		if (!Class){

			throw J.createError( 'cannot call $my outside the class methods' );

			return nil;

		}

		return Class.prototype;

	},

	initializeCache: function(cache){
		
		Jx.assign(cache, cache);
		
	},

	onDestroy: function(){},

	onSet: function(name, value){

		this.$cache[name] = value;

	},

	isDestroyed: function( includeDestructorProcess ){

		return this.destroyed ||

			(includeDestructorProcess !== false && this.destroying);

	},

	destroy: function(){

		if (!this.destroying && !this.destroyed){

			this.destroying = true;

			this.onDestroy();

			//-- remove cache values
			Jx.destroy(this.$cache);

			//-- finale
			Jx.each(this, function(n, v, o){

				if (o.hasOwnProperty(n)){

					delete o[n];

				}

			});

		}

		return this;

	},

/**
 *	basic accessors
 */

	get: function(name){

		var o = this.$cache, nil;

		return Jx.isString(name, true) && Jx.isObject(o) && name in o ?

			o[name]

			:

			nil;

	},

	set: function(name, value){

		if (Jx.isObject( this.$cache ) &&

			this.isValid(name, value)

		){

			this.onSet(name, value);

		}

		return this;

	},

	isSet: function(name){

		var o = this.$cache;

		return Jx.isString(name, true) && Jx.isObject(o) && o.hasOwnProperty(name);

	},

	isValid: function(name, value){

		return Jx.isString(name, true);

	},

	toString: function(){

		return '[class '+this.constructor.$name+']';

	}

});

})(Jx, Object.prototype, Array.prototype);
